package query

import (
	"context"
	"fmt"

	"code.byted.org/bytertc/whiteboard_server/biz/dal"
	"code.byted.org/bytertc/whiteboard_server/biz/dal/spec"
	"github.com/spf13/cast"

	v1 "code.byted.org/bytertc/whiteboard_server/biz/api/pb/v1"
	"code.byted.org/bytertc/whiteboard_server/biz/service/command"
	"code.byted.org/bytertc/whiteboard_server/biz/service/command/base"
)

const InvalidBoardId = -1

func NewGetBoardInfoCommand(ctx context.Context, cmd *base.Command, boardIds []int32) *boardInfoCommand {
	return &boardInfoCommand{
		Command:  *cmd,
		boardIds: boardIds,
	}
}

type boardInfoCommand struct {
	base.Command
	boardIds []int32
}

func (b *boardInfoCommand) Execute(ctx context.Context) []command.Result {
	resp, err := b.execute(ctx)
	return []command.Result{&base.Result{
		Msg:         resp,
		IsBroadcast: false,
		CommandType: v1.Command_kGetBoardInfo,
		Error:       err,
	}}
}

func genBoardInfosKey(unique spec.RoomUnique, boardIds []int32) string {
	var boardIdsStr string
	for _, id := range boardIds {
		boardIdsStr += cast.ToString(id) + "-"
	}
	return fmt.Sprintf("boardInfos:%s:%s:%s",
		unique.GetAppId(), unique.GetRoomId(), boardIdsStr)
}

func (b *boardInfoCommand) execute(ctx context.Context) (resp *v1.GetBoardInfoResponse, err error) {
	ret, err, _ := dal.MSingleflight.Do(genBoardInfosKey(b, b.boardIds), func() (interface{}, error) {
		boardInfos, err := b.Repo.GetBoards(ctx, b, b.boardIds)
		if err != nil {
			return nil, err
		}
		resp := &v1.GetBoardInfoResponse{}

		iter := newIter(b.Info, boardInfos)
		for iter.Next() {
			//1.3版本会用到首个page
			pageInfos, err := b.Repo.GetPages(ctx, iter, nil)
			if err != nil {
				return nil, err
			}
			rawPageInfos := make([]*v1.PageInfo, 0, len(pageInfos))
			for _, page := range pageInfos {
				boardInfos[iter.index].ControlCount[page.PageId] = page.ControlCount
				rawPageInfos = append(rawPageInfos, page.PageInfo)
			}
			boardInfos[iter.index].Pages = rawPageInfos
			if boardInfos[iter.index].Status == v1.BoardInfo_Inactive {
				continue
			}

			cursorInfo, err := b.Repo.GetTopNCursorState(ctx, iter)
			if err != nil {
				return nil, err
			}
			boardInfos[iter.index].UserCursorInfo = cursorInfo

			info, err := b.Repo.GetZoomInfo(ctx, iter)
			if err != nil {
				return nil, err
			}
			boardInfos[iter.index].ZoomInfo = info

			pptMediaInfo, err := b.Command.Repo.GetPptMediaInfo(ctx, iter, iter.GetCurrentPageID())
			if err != nil {
				return nil, err
			}
			boardInfos[iter.index].PptCurrentMedias = pptMediaInfo

			pptTriggerInfo, err := b.Command.Repo.GetPptTriggerInfo(ctx, iter, iter.GetCurrentPageID())
			if err != nil {
				return nil, err
			}
			boardInfos[iter.index].PptCurrentTriggers = pptTriggerInfo

		}
		resp.Boards = boardInfos
		room, err := b.Repo.GetRoom(ctx, b)
		if err != nil {
			return nil, err
		}
		if room == nil {
			id := int32(InvalidBoardId)
			if len(boardInfos) > 0 {
				id = boardInfos[0].BoardId
			}
			resp.ActiveBoardId = &id
		} else {
			resp.ActiveBoardId = &room.ActiveBoardId
			status := v1.GetBoardInfoResponse_StatusType(room.Status)
			resp.RoomStatus = &status
		}
		packetId, err := b.Repo.GetPacketId(ctx, b)
		if err != nil {
			return nil, err
		}
		resp.PacketId = packetId

		return resp, nil
	})
	if err != nil {
		return nil, err
	}
	resp = ret.(*v1.GetBoardInfoResponse)
	return resp, nil
}

type boardInfoIterator struct {
	base.Info
	boardInfos []*v1.BoardInfo
	index      int
}

func newIter(info base.Info, boardInfos []*v1.BoardInfo) *boardInfoIterator {
	return &boardInfoIterator{
		Info:       info,
		boardInfos: boardInfos,
		index:      -1,
	}
}

func (b *boardInfoIterator) GetBoardId() int32 {
	return b.boardInfos[b.index].BoardId
}

func (b *boardInfoIterator) GetCurrentPageID() string {
	return b.boardInfos[b.index].CurrentPageId
}

func (b *boardInfoIterator) Next() bool {
	b.index++
	return b.index < len(b.boardInfos)
}
